﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Diagnostics;
using System.Linq;
using System.Text;
using Csla;
using Csla.Core;
using Csla.Data;
using lite;
using OpenExpressApp;
using OpenExpressApp.DB.Utils;
using OpenExpressApp.MetaAttribute;
using OpenExpressApp.MetaModel;
using OpenExpressApp.ORM;
using OpenExpressApp.Types;

namespace OpenExpressApp.Module.Library
{
    [Serializable]
    public partial class UserList : EntityList<UserList, User>
    {
        #region  Factory Methods

        public static UserList GetList()
        {
            return DataPortal.Fetch<UserList>();
        }

        protected override bool AsChild
        {
            get
            {
                return false;
            }
        }

        private UserList() { }

        internal static User Get(string code, string password)
        {
            using (var db = DBHelper.CreateDb(ConnectionStringNames.OpenExpressApp))
            {
                IQuery q = db.Query().Constrain("Code").Equal(code)
                    .And().Constrain("Password").Equal(password);
                IList<User> users = db.Select<User>(q);

                return users.Count == 0 ? null : User.GetLazy(users[0].Id);
            }
        }

        #endregion

        #region  Data Access

        private void DataPortal_Fetch()
        {
            RaiseListChangedEvents = false;

            using (var db = DBHelper.CreateDb(ConnectionStringNames.OpenExpressApp))
            {
                IQuery q = db.Query();
                var list = db.Select<User>(q);
                foreach (var item in list)
                {
                    this.Add(User.GetLazy(item.Id));
                }
            }

            RaiseListChangedEvents = true;
        }

        #endregion
    }

    [Serializable]
    [Table]
    [DefaultObject("9DE11439-4C23-4F66-9E6E-2574C2639DA6", Catalog = "用户权限", Index = 2), Label("用户管理")]
    public partial class User : Entity<User>
    {
        #region  Business Methods

        private static PropertyInfo<string> CodeProperty =
            RegisterProperty(new PropertyInfo<string>("Code"));
        [Column]
        [EntityProperty]
        [Required, ShowInList, ShowInDetail, Label("登录代号"), ShowInLookup]
        public string Code
        {
            get { return GetProperty(CodeProperty); }
            set { SetProperty(CodeProperty, value); }
        }

        private static PropertyInfo<string> NameProperty =
          RegisterProperty(new PropertyInfo<string>("Name"));
        [Column]
        [EntityProperty]
        [Required, IsTitle, ShowInList, ShowInDetail, Label("姓名")]
        public string Name
        {
            get { return GetProperty(NameProperty); }
            set { SetProperty(NameProperty, value); }
        }

        private static PropertyInfo<string> PasswordProperty =
            RegisterProperty(new PropertyInfo<string>("Password"));
        [Column]
        [EntityProperty]
        [Required, IsTitle, Label("密码")]
        [Editor(EditorNames.Password)]
        public string Password
        {
            get { return GetProperty(PasswordProperty); }
            set { SetProperty(PasswordProperty, value); }
        }

        private static PropertyInfo<DateTime?> LastLoginTimeProperty =
            RegisterProperty(new PropertyInfo<DateTime?>("LastLoginTime"));
        [Column]
        [EntityProperty]
        [Required, Label("最后登录时间")]
        public DateTime? LastLoginTime
        {
            get { return GetProperty(LastLoginTimeProperty); }
            set { SetProperty(LastLoginTimeProperty, value); }
        }

        private static PropertyInfo<int> LoginCountProperty =
            RegisterProperty(new PropertyInfo<int>("LoginCount", int.MaxValue));
        [Column]
        [EntityProperty]
        [Required, Label("剩余登录次数")]
        [Editor(EditorNames.Password)]
        public int LoginCount
        {
            get { return GetProperty(LoginCountProperty); }
            set { SetProperty(LoginCountProperty, value); }
        }

        private static PropertyInfo<int> MaxLoginCountProperty =
            RegisterProperty(new PropertyInfo<int>("MaxLoginCount"));
        [Column]
        [EntityProperty]
        public int MaxLoginCount
        {
            get { return GetProperty(MaxLoginCountProperty); }
            set { SetProperty(MaxLoginCountProperty, value); }
        }

        #endregion

        #region  Factory Methods

        public static User Get(Guid id)
        {
            return DataPortal.Fetch<User>(new SingleLazyCriteria<User, Guid>(id, false));
        }

        internal static User GetLazy(Guid id)
        {
            return DataPortal.Fetch<User>(new SingleLazyCriteria<User, Guid>(id, true));
        }

        #endregion

        #region  Data Access

        private void DataPortal_Fetch(SingleLazyCriteria<User, Guid> criteria)
        {
            using (var db = this.CreateDb())
            {
                IQuery q = db.Query();
                q.Constrain("Id").Equal(criteria.Value);
                var data = db.Select<User>(q)[0];

                this.Clone(data, false);

                this.IsObjectLazy = criteria.IsObjectLazy;
            }
        }

        [Transactional(TransactionalTypes.TransactionScope)]
        protected override void DataPortal_Insert()
        {
            using (var db = this.CreateDb())
            {
                this.CheckUniqueCode(db);

                db.Insert(this);
                // update child objects
                FieldManager.UpdateChildren(this);
            }
        }

        [Transactional(TransactionalTypes.TransactionScope)]
        protected override void DataPortal_Update()
        {
            using (var db = this.CreateDb())
            {
                this.CheckUniqueCode(db);

                db.Update(this);
                // update child objects
                FieldManager.UpdateChildren(this);
            }
        }

        private void CheckUniqueCode(IDb db)
        {
            string sql = string.Format("select 1 from [User] where Code = '{0}' and Id != '{1}'", this.Code, this.Id);
            if (db.ExecSql(sql).Rows > 0)
            {
                throw new Exception("已经有这个编码的用户了。");
            }
        }

        #endregion

        #region Business Methods

        /// <summary>
        /// 判断用户是否可以登录
        /// </summary>
        /// <param name="user">user</param>
        /// <returns></returns>
        public bool TryLogin()
        {
            OrgPositions positions = OrgPositions.GetList(this.Id);
            if (positions.Count == 0 || positions.Any(c => c.LoginTotalCount == 0))
            {
                return true;
            }

            //有最大次数的position
            var maxCountPosition = positions.FirstOrDefault(p => positions.All(p2 => p2.LoginTotalCount <= p.LoginTotalCount));

            var servertime = new LazyServerTime();

            if (maxCountPosition != null)
            {
                //如果刚加了新的position，或者更改了最大登录次数
                if (this.MaxLoginCount != maxCountPosition.LoginTotalCount ||
                    //如果是第一次登录
                    this.LastLoginTime.HasValue == false ||
                    //上次登录时间到现在已经超过一个星期，则重置可用的登录次数
                    (servertime.Time - this.LastLoginTime.Value).Days >= 7
                    )
                {
                    this.LoginCount = maxCountPosition.LoginTotalCount;
                    this.MaxLoginCount = maxCountPosition.LoginTotalCount;
                }
            }

            //需要检测LoginCount
            if (this.LoginCount > 0)
            {
                this.LastLoginTime = servertime.Time;
                this.LoginCount--;
                this.Save();

                return true;
            }

            return false;
        }

        /// <summary>
        /// 延迟加载服务端时间的类
        /// </summary>
        private class LazyServerTime
        {
            private DateTime? _time;

            public DateTime Time
            {
                get
                {
                    if (this._time == null)
                    {
                        try
                        {
                            this._time = OpenExpressApp.Library.DogSensor.ServerTime.Get();
                        }
                        catch
                        {
                            this._time = DateTime.Now;
                        }
                    }
                    return this._time.Value;
                }
            }
        }

        /// <summary>
        /// 获取用户对应的岗位的使用时间
        /// 如果对应多个岗位，取最大的使用时间
        /// </summary>
        /// <param name="user">user</param>
        /// <returns></returns>
        public static TimeSpan GetAvailableTime(User user)
        {
            double hour = 0;

            var positions = OrgPositions.GetList(user.Id);
            if (positions.Count > 0 && positions.All(c => c.Position.UseTime != 0))
            {
                hour = positions.Max(c => c.Position.UseTime);
            }

            var tick = (long)(hour * 60 * 60 * 1000);
            return new TimeSpan(tick);
        }

        #endregion
    }
}